#define GPFSEL1		0x20200004U
#define GPCLR0		0x20200028U
#define GPSET0		0x2020001CU

#define IRQ_BASIC_PENDING	0x2000B200U
#define IRQ_PENDING_1		0x2000B204U
#define IRQ_PENDING_2		0x2000B208U
#define FIQ_CONTROL			0x2000B20CU
#define ENABLE_IRQ_1		0x2000B210U
#define ENABLE_IRQ_2		0x2000B214U
#define ENABLE_BASIC_IRQ	0x2000B218U
#define DISABLE_IRQ_1		0x2000B21CU
#define DISABLE_IRQ_2		0x2000B220U
#define DISABLE_BASIC_IRQ	0x2000B224U

#define SYS_TIMER_CS		0x20003000U
#define SYS_TIMER_CLO		0x20003004U
#define SYS_TIMER_CHI		0x20003008U
#define SYS_TIMER_C0		0x2000300CU
#define SYS_TIMER_C1		0x20003010U
#define SYS_TIMER_C2		0x20003014U
#define SYS_TIMER_C3		0x20003018U


extern void Set32(unsigned int addr, unsigned int value);
extern unsigned int Get32(unsigned int addr);
extern void EnableIRQ(void);
extern void EnableFIQ(void);


/**************************************************
 * Gestion de la LED
 **************************************************/

unsigned int ledStatus = 0U;

void TurnLedOn(void)
{
	// On allume la led
	unsigned int *gpclr0_ptr = ((unsigned int*)GPCLR0);
	*gpclr0_ptr = *gpclr0_ptr | 0x10000U;
	ledStatus++;
}

void TurnLedOff(void)
{
	// On éteint la led
	unsigned int *gpclr0_ptr = ((unsigned int*)GPSET0);
	*gpclr0_ptr = *gpclr0_ptr | 0x10000U;
	ledStatus++;
}

void SwitchLed(void)
{
	if ((ledStatus & 1U) == 0U)
	{
		TurnLedOff();
	}
	else
	{
		TurnLedOn();
	}
}

void InitLed(void)
{
	// On active la fonction du 1 du pin 16 du GPIO
	unsigned int *gpfsel1_ptr = ((unsigned int*)GPFSEL1);
	*gpfsel1_ptr = *gpfsel1_ptr | 0x40000U;
	TurnLedOff();
}


/******************************************************
* Géstion des IRQs
* ****************************************************/

volatile int irqCount = 0U;

void IRQHandler(void)
{
	SwitchLed();
	Set32(SYS_TIMER_CS, (1U << 1U));
	Set32(SYS_TIMER_C1, Get32(SYS_TIMER_CLO) + 2000000U);
	if ((ledStatus & 1U) != 0U)
	{
		irqCount++;
	}
}

void FIQHandler(void)
{
	SwitchLed();
	Set32(SYS_TIMER_CS, (1U << 1U));
	Set32(SYS_TIMER_C1, Get32(SYS_TIMER_CLO) + 200000U);
}


/*****************************************************
 * Fonction kmain
 *****************************************************/
int kmain(void)
{
	InitLed();

	// On intialise le timer
	Set32(SYS_TIMER_CS, (1U << 0U) | (1U << 1U) | (1U << 2U));

	// On active les IT
	// C1 est lié à IRQ1 et C3 à IRQ3
	Set32(ENABLE_IRQ_1, (1U << 1U));

	/*
	// On active les IRQ
	EnableIRQ();

	// On utilise le timer
	Set32(SYS_TIMER_C1, Get32(SYS_TIMER_CLO) + 2000000U);

	while (irqCount < 3)
	{
		// RAF
	}
	*/

	// On recommance, avec des FIQ !
	Set32(FIQ_CONTROL, (1U << 7U) | 1U);

	// On active les FIQ
	EnableFIQ();

	// On utilise le timer
	Set32(SYS_TIMER_C1, Get32(SYS_TIMER_CLO) + 2000000U);

	for ( ;; )
	{
		// RAF
	}

	return 0;
}
